
if(!window.Berta) window.Berta = {};

//*** NOTE *** when defining variables in the prototype of an ABSTRACT (or any other sub-) class
// don't instantiate objects and arrays directly. instantiate them inside the constructor.
// otherwise they become part of superclass prototype and take the same value for all subclasses

var ___defaultSaveFunc = function(rest, data, callback) {	
	$.log(this.pluginName +" (" + this.id + ") has no save function!\n" +
		  "Attempted command: " + rest);
	return 0; 	// returns identifier by which the saving process can be identified
};
var ___defaultSaveStopFunc = function(processId) {
	$.log(this.pluginName +" (" + this.id + ") has no saveStop function!\n" +
		  "Process to stop: " + processId);
};

Berta.Editor = new /* ABSTRACT */ Class(function(){
    return {
		

		// statics
		statics: {
			/* private */ flags: {		// FLAGS, used internally
				ON: 'ON',				// state flags
				EDITING: 'EDITING',
				SAVING: 'SAVING',
				DESTROYING: 'DESTROYING'
			},
			
			DATA_REPLACEMENT: '_berta_Replacement_ID',
			
			PANEL_CONTEXT: 'panel',
			PAGE_CONTEXT: 'page'
		},
		
		// defaults for all Berta.Editor types
		defaults: {
			
			type: null,						// the plugin type (Textline, Select, File, Gallery, etc)
			element: null,					// element to edit
			variable: null,					// REST variable
			params: null,					// object (key=>value) of params to be sent along in the POST
			
			context: 'page',				// context of the editor ('page' or 'panel')
			
			// save and saveStop functions 
			// that must refer to methods in the data layer
			save: ___defaultSaveFunc,			// MUST BE OVERRIDEN!
			saveStop: ___defaultSaveStopFunc,	// MUST BE OVERRIDEN!
			
			// callbacks (events) for Berta.EditorManager use
			callbacks: {
				onSave: function() { },			// save callback
				onDestroy: function() { }		// destroy callback
			},
			
			// plugin specific options
			// plugin callbacks can be used from outer interface
			pluginOptions: {
				caption: null,
				callbacks: {
					onChange: null,
					onSave: null,
					onRevert: null
				}
			}
		},
		
		// a GUID for editor instance (each editor has an unique GUID)
		id: null,
		
		// plugin name is overriden by each plugin
		pluginName: '',
		
		// getter: returns event namespace for the instance
		namespace: function() {
			return '.Berta.Editor.' + (this.pluginName ? (this.pluginName + '.') : '') + this.id;
		},
		
		// the element that gets edited
		element: null,
		
		value : '',				// value/contents of the element
		originalValue: '',		// value/contents of the element in the beginning
		
		
		constructor: function(options) {
			// generate the GUID
			//$.log(options);
			
			this.id = $.uniqid.generate();
			this._(initFlags)();
			
			// assign element
			this.element = options.element;
			delete options.element;
			
			// setting options
			this.options = $.extend(true, {}, Berta.Editor.prototype.defaults, this.defaults, options);
			
			// validate options
			this.validate();
		},
		
		
		//   .______    __    __  .______    __       __    ______    ////////////////////////////////////////////////////////////
		//   |   _  \  |  |  |  | |   _  \  |  |     |  |  /      |   ////////////////////////////////////////////////////////////
		//   |  |_)  | |  |  |  | |  |_)  | |  |     |  | |  ,----'   ////////////////////////////////////////////////////////////
		//   |   ___/  |  |  |  | |   _  <  |  |     |  | |  |        ////////////////////////////////////////////////////////////
		//   |  |      |  `--'  | |  |_)  | |  `----.|  | |  `----.   ////////////////////////////////////////////////////////////
		//   | _|       \______/  |______/  |_______||__|  \______|   ////////////////////////////////////////////////////////////

		
		validate: function() {
			this.options.element = $(this.options.element);
			//var v = this.options.variable.split(':');
		},
		
		
		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		//  API   //////////////////////////////////////////////////////////////////////////////////////////////////////////////
		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	
		on: function() {
			this.setFlag(Berta.Editor.flags.ON);
		},
		
		off: function() {
			this.removeFlag(Berta.Editor.flags.ON);
			if (this.hasFlag(Berta.Editor.flags.DESTROYING)) {
				this.garbageCollect();
			}
		},
		
		edit: function() {
			
		},
		
		save: function(saveInternalCallback) {
			// POSTS this.value to this.rest
			// calls saveComplete on ready
			
			//$.log('Berta.Editor: saving... "' + this.value + '"');
			this.setFlag(Berta.Editor.flags.SAVING);
			
			this.saveInternalCallback = saveInternalCallback;

			this.saveProcessId = this.options.save(
				
				// Pass route
				this.options.route,
				this.options.route_variables,
				
				// Pass variable and value
				this.options.variable, 
				this.value,
				
				// Pass additional params
				this.options.params,
				
				// Complete callback
				this._(this.saveComplete)	// use _ function to have correct "this" variable in this.saveComplete
			);
		},
		
		cancel: function() {
			if(this.hasFlag(Berta.Editor.flags.DESTROYING)) {
				this.off();
			}
		},
		
		destroy: function(doSave) {
			this.setFlag(Berta.Editor.flags.DESTROYING);
			if (this.hasFlag(Berta.Editor.flags.EDITING)) {
				if (doSave) {
					this.save();
				} else {
					this.cancel();
				}
			} else if (this.hasFlag(Berta.Editor.flags.SAVING)) {
				if(!doSave) this.cancel();
			} else {
				this.off();
			}
		},
		garbageCollect: function() {
			this.element = null;
			this.options = null;
			this.defaults = null;
		},
		
		getValue: function() {
			return this.value;
		},
		
		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		//  CALLBACKS   ////////////////////////////////////////////////////////////////////////////////////////////////////////
		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		fireChange: function() {
			!this.options.callbacks.onChange || this.options.callbacks.onChange(this);
		},
		fireSave: function() {
			!this.options.callbacks.onSave || this.options.callbacks.onSave(this);
		},
		fireRevert: function() {
			!this.options.callbacks.onRevert || this.options.callbacks.onRevert(this);
		},
		
		
		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		//  PANEL CONTEXT   ////////////////////////////////////////////////////////////////////////////////////////////////////
		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		
		panelContextSet: false,
		
		setPanelContext: function() {
			if(!this.panelContextSet 
					&& this.options.contextSettings
					&& this.options.contextSettings.length > 0) {
				this.panelContextSet = true;
				Berta.Panel.instance().showContext(this.options.contextSettings);
			}

		},
		hidePanelContext: function() {
			if(this.panelContextSet) {
				Berta.Panel.instance().hideContext();
				this.panelContextSet = false;
			}
		},
		
			
		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		//  ENABLING AND DISABLING EDITING   ///////////////////////////////////////////////////////////////////////////////////
		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		
		startEditing: function() { this.setFlag(Berta.Editor.flags.EDITING); },
		stopEditing: function() { this.removeFlag(Berta.Editor.flags.EDITING); },
		
		
		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		//  SAVING   ///////////////////////////////////////////////////////////////////////////////////////////////////////////
		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		
		saveProcessId: null,
		saveInternalCallback: null,
		
		saveStop: function() {
			this.options.saveStop(this.saveProcessId);
			this.saveProcessId = null;
		},
		saveComplete: function(response) {
			this.saveProcessId = null;
			this.removeFlag(Berta.Editor.flags.SAVING);
			
			// Internal cvallback is a function inside the plugin that does the stuff after save
			if(jQuery.isFunction(this.saveInternalCallback)) {
				this.saveInternalCallback(response);
			
			} else if(!response.error) {
				// Call onSave callback only when there is no internal callback
				// Otherwise rely on the plugin to call onSave
				this.options.callbacks.onSave ? this.options.callbacks.onSave(this) : false;
			}
		},
		
		
		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		//  UTILITY   //////////////////////////////////////////////////////////////////////////////////////////////////////////
		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		
		askIfCancelOnSaveErorr: function(errorText, callbackYes, callbackNo) {
			var cancelClicked = false;
			$('<div id="dialog-confirm" title="Error!">' + 
				'<p><span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 20px 0;"></span>' + 
					'Berta could not save the changes you made in this field.<br/>' +
					(errorText ? errorText + '<br />&nbsp;<br />' : '') +  
					'Do you want to continue editing?' +
				'</p></div>').dialog({
				resizable: false,
				height:160,
				modal: true,
				zIndex: 2000000,
				buttons: {
					'Cancel editing': function(event, ui) {
						event.preventDefault();
						event.stopPropagation();
						cancelClicked = true;
						$(this).dialog('close');
					},
					'Continue': function(event, ui) {
						event.preventDefault();
						event.stopPropagation();
						$(this).dialog('close');
					}
				},
				close: function(event, ui) {
					if(cancelClicked) {
						if(jQuery.isFunction(callbackYes)) callbackYes();
					} else {
						if(jQuery.isFunction(callbackNo)) callbackNo();
					}
				}
			});
		},
		
		getEmptyPlaceholder: function(caption) {
			//$.log(this.options.context, caption);
			var c;
			if(caption) {
				c = caption;
			} else if(this.options.pluginOptions.caption) {
				c = this.options.pluginOptions.caption;
			} else if(this.options.context === 'panel') {
				c = '&nbsp; &nbsp;';
			} else {
				c = this.options.variable;
			}

			return $('<span class="x-editable-placeholder">' + c + '</span>');
		},
		appendPlaceholderIfEmpty: function(el, caption, emptyDetect) {
			if(!el) el = this.element;
			if(!emptyDetect) emptyDetect = this._(detectIfElementIsEmpty);
			if(emptyDetect(el)) {
				el.append(this.getEmptyPlaceholder(caption)).addClass('x-editable-empty');
			}
		},
		removePlaceholder: function(el) {
			if(!el) el = this.element;
			el.removeClass('x-editable-empty').find('.x-editable-placeholder').remove();
		},
		
		
		
		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		//  PROTECTED FUNCTIONS   //////////////////////////////////////////////////////////////////////////////////////////////
		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		hasFlag: /* protected */ function(flagName) {
			return (flagName in this._flags) && this._flags[flagName];
		},
		setFlag: /* protected */ function(flagName) {
			this._flags[flagName] = true;
		},
		removeFlag: /* protected */ function(flagName) {
			this._flags[flagName] = false;
		}
		
		
    };
	
	
	
	
	
	//   .______   .______     __  ____    ____  ___   .___________. _______    //////////////////////////////////////////////////
	//   |   _  \  |   _  \   |  | \   \  /   / /   \  |           ||   ____|   //////////////////////////////////////////////////
	//   |  |_)  | |  |_)  |  |  |  \   \/   / /  ^  \ `---|  |----`|  |__      //////////////////////////////////////////////////
	//   |   ___/  |      /   |  |   \      / /  /_\  \    |  |     |   __|     //////////////////////////////////////////////////
	//   |  |      |  |\  \--.|  |    \    / /  _____  \   |  |     |  |____    //////////////////////////////////////////////////
	//   | _|      | _| `.___||__|     \__/ /__/     \__\  |__|     |_______|   //////////////////////////////////////////////////
	
	
	function initFlags() {
		this._flags = {};
	}
	
	function detectIfElementIsEmpty(el) {
		var h = el.html();
		h = h.replace(/\&nsp;/, '');
		if(h === '' || jQuery.trim(h) === '') {
			return true;
		}
		return false;
	}

	
}());
